package com.slliver.controller;


import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
@RestController
@RequestMapping("/sentinel/test")
public class TestController {

    private static final String RESOURCE_NAME = "hello";


    /**
     * 为资源定义流量控制规则
     */
    @PostConstruct  // spring加载bean的时候调用此方法
    private static void initFlowRules() {

        // 通过代码定义流量控制规则
        // 流控规则
        List<FlowRule> rules = new ArrayList<>();

        // 定义一个流程
        FlowRule rule = new FlowRule();
        // 设置受流量控制保护的资源(这里就是我们的hello)
        rule.setResource(RESOURCE_NAME);
        // 设置流控规则QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值，简单的说 每秒可以被访问的次数
        // Set limit QPS to 20
        rule.setCount(1);
        rules.add(rule);

        // 加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }


    /**
     * 参数说明：
     * value        定义受管控的资源
     * blockHandler 设置被流控降级后的处理方法，默认该方法必须声明在同一个类中
     * fallback     处理异常使用
     * exceptionsToTrace    排查哪些异常不处理,不处理就不会进入fallback指定的方法
     * 如果blockHandler和fallback同时指定，那么blockHandler的优先级更高
     */
    @GetMapping("/hello")
    @SentinelResource(value = RESOURCE_NAME, blockHandler = "blockHandlerForHello", fallback = "fallbackForHello", exceptionsToIgnore = ArithmeticException.class)
    public String hello(String id) {
        // 正常的业务操作
        if (Objects.equals("123", id)) {
            // int a = 100 / 0;
        }
        return "调用hello方法成功，可以正常访问...";
    }

    /**
     * 注意几点：
     * 1、该方法一定是public的
     * 2、该方法的返回值一定要和调用的方法的返回值一样，比如这里的hello资源被流控，返回值和hello方法一致, 方法的参数和顺序也要和源方法一致
     */
    // 原方法调用被限流的时候调用
    public String blockHandlerForHello(String id, BlockException ex) {
        log.error("调用hello方法失败 ==>> {}", ex);
        return "调用hello方法失败，被限流操作了";
    }

    // 原方法调用出现异常的处理方法
    public String fallbackForHello(String id, Throwable e) {
        log.error("调用hello方法异常 ==>> {}", e);
        return "调用hello方法失败，发生异常...";
    }
}
